﻿; (function () {

    'use strict';

    window.SolvingHelper = {};

    SolvingHelper.removeFromOtherCells = function (candidate, keepCells, cellGroup) {
        var removed = false;
        for (var i = 0, len = cellGroup.length; i < len; i++) {
            var cell = cellGroup[i];
            if (!keepCells.contains(cell)) {
                removed |= cell.candidates.remove(candidate);
            }
        }
        return removed;
    }

    SolvingHelper.removeRangeFromOtherCells = function (candidates, keepCells, cellGroup) {
        var removed = false;
        var candidatesLength = candidates.length;
        for (var i = 0, len = cellGroup.length; i < len; i++) {
            var cell = cellGroup[i];
            if (!keepCells.contains(cell)) {
                for (var canIndex = 0; canIndex < candidatesLength; canIndex++) {
                    removed |= cell.candidates.remove(candidates[canIndex]);
                }
            }
        }
        return removed;
    }

    SolvingHelper.removeDependingCandidates = function (cell, grid) {

        for (var i = 0; i < 9; i++) {
            // row
            grid.cells[cell.indices.i][i].candidates.remove(cell.value);
            // column
            grid.cellsByColumn[cell.indices.j][i].candidates.remove(cell.value);
            //region
            var regionId = RegionHelper.computedRegions[cell.indices.i][cell.indices.j];
            grid.cellsByRegion[regionId][i].candidates.remove(cell.value);
        }
    }

    SolvingHelper.fillCandidates = function (grid) {
        for (var i = 0; i < 9; i++) {
            for (var j = 0; j < 9; j++) {
                var cell = grid.cells[i][j];
                SolvingHelper.fillCandidatesForCell(grid, cell);
            }
        }
    }

    SolvingHelper.fillCandidatesForDependingCells = function (grid, cell) {
        SolvingHelper.fillCandidatesForCells(grid, grid.cells[cell.indices.i]);
        SolvingHelper.fillCandidatesForCells(grid, grid.cellsByColumn[cell.indices.j]);

        var regionId = RegionHelper.computedRegions[cell.indices.i][cell.indices.j];
        SolvingHelper.fillCandidatesForCells(grid, grid.cellsByRegion[regionId]);
    }

    SolvingHelper.fillCandidatesForCells = function (grid, cells) {
        for (var i = 0; i < 9; i++) {
            var cell = cells[i];
            SolvingHelper.fillCandidatesForCell(grid, cell);
        }
    }

    SolvingHelper.fillCandidatesForCell = function (grid, cell) {
        cell.candidates.clear();
        if (cell.value != 0)
            return;

        for (var k = 1; k < 10; k++) {
            if (SolvingHelper.isCandidateEligible(k, cell.indices, grid))
                cell.candidates.add(k);
        }
    }

    // indexation on value is not worth it, because fillcandidate is called only one is solve process(index maintain cost)
    SolvingHelper.isCandidateEligible = function (num, indices, grid) {

        if (SolvingHelper.isValuerOnRow(num, indices.i, grid))
            return false;

        if (SolvingHelper.isValueOnColumn(num, indices.j, grid))
            return false;

        var regionId = RegionHelper.computedRegions[indices.i][indices.j];
        if (SolvingHelper.isValueOnRegion(num, regionId, grid))
            return false;

        return true;
    }

    SolvingHelper.isValueOnRegion = function (value, regionId, grid) {
        return SolvingHelper.isValueOnUnit(value, grid.cellsByRegion[regionId]);
    }

    SolvingHelper.isValuerOnRow = function (value, row, grid) {
        return SolvingHelper.isValueOnUnit(value, grid.cells[row]);
    }

    SolvingHelper.isValueOnColumn = function (value, column, grid) {
        return SolvingHelper.isValueOnUnit(value, grid.cellsByColumn[column]);
    }

    SolvingHelper.isValueOnUnit = function (value, unit) {
        for (var i = 0; i < 9; i++) {
            var cell = unit[i];
            if (cell.value === value)
                return true;
        }
        return false;
    }

    SolvingHelper.getEmptyCells = function (cells) {
        var emptyCells = [];
        for (var i = 0, len = cells.length; i < len; i++) {
            var cell = cells[i];
            if (cell.value === 0)
                emptyCells.push(cell);
        }
        return emptyCells;
    }


    SolvingHelper.getDistinctCandidates = function (cells) {

        var possibleCandidates = [];
        for (var i = 0, l = cells.length; i < l; i++) {
            var cell = cells[i];
            for (var v = 0 ; v < cell.candidates.count; v++) {
                var candidate = cell.candidates.values[v];

                if (!possibleCandidates.contains(candidate)) {
                    possibleCandidates.push(candidate);
                }
            }
        }

        return possibleCandidates;
    }

    SolvingHelper.IsOnSameUnitAs = function (cell, otherCell) {

        if (cell.indices.i === otherCell.indices.i) {
            return 'row'
        }

        if (cell.indices.j === otherCell.indices.j) {
            return 'column'
        }

        var regionId = RegionHelper.computedRegions[cell.indices.i][cell.indices.j];
        var otherRegionId = RegionHelper.computedRegions[otherCell.indices.i][otherCell.indices.j];
        if (regionId === otherRegionId) {
            return 'region'
        }

        return null;
    }

}(window));
